package top.lingkang.hibernate5.dao;

import org.hibernate.SessionFactory;
import org.noear.solon.Solon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.lingkang.hibernate5.dao.impl.DaoQueryHandler;
import top.lingkang.hibernate5.dao.impl.ListQueryHandler;
import top.lingkang.hibernate5.dao.impl.ModifyingQueryHandler;
import top.lingkang.hibernate5.dao.impl.OneQueryHandler;
import top.lingkang.hibernate5.util.CheckUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * @author lingkang
 * created by 2023/9/28
 * @since 1.0.0
 */
public class DaoInvocationHandler implements InvocationHandler {
    private static final Logger log = LoggerFactory.getLogger(DaoInvocationHandler.class);
    private Class<?> daoInterface;
    private Class<?> entityClass;
    private SessionFactory sessionFactory;
    private Map<Method, QueryHandler> handlerCache = new HashMap<>();

    public DaoInvocationHandler(Class<?> daoInterface, Class<?> entityClass) {
        this.daoInterface = daoInterface;
        this.entityClass = entityClass;
    }

    @SuppressWarnings("deprecation")
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        QueryHandler queryHandler = handlerCache.get(method);
        if (queryHandler != null)
            return queryHandler.doQuery(method, args);


        if (method.getDeclaringClass() == HibernateDao.class) {
            DaoQueryHandler handler = new DaoQueryHandler(entityClass);
            Object result = handler.doQuery(method, args);
            handlerCache.put(method, handler);
            return result;
        }

        // 检查回话工厂
        checkSessionFactory();

        Query query = method.getAnnotation(Query.class);
        CheckUtils.checkNotNull(query, "Dao 接口方法无 @Query 注解：" + daoInterface.getName() + "." + method.getName());
        Modifying modifying = method.getAnnotation(Modifying.class);

        // 类型处理
        Class<?> returnType = method.getReturnType();
        Type type = method.getGenericReturnType();
        boolean isList = false;
        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            returnType = (Class<?>) pt.getActualTypeArguments()[0];
            isList = true;
        }

        if (modifying == null) {
            if (isList) {
                QueryHandler handler = new ListQueryHandler(sessionFactory, returnType, query, query.nativeQuery());
                Object result = handler.doQuery(method, args);
                handlerCache.put(method, handler);
                return result;
            } else {
                OneQueryHandler handler = new OneQueryHandler(sessionFactory, returnType, query, query.nativeQuery());
                Object result = handler.doQuery(method, args);
                handlerCache.put(method, handler);
                return result;
            }
        } else {
            ModifyingQueryHandler handler = new ModifyingQueryHandler(sessionFactory, query, query.nativeQuery(), returnType);
            Object result = handler.doQuery(method, args);
            handlerCache.put(method, handler);
            return result;
        }
    }

    private void checkSessionFactory() {
        if (sessionFactory == null) {
            sessionFactory = Solon.context().getBean(SessionFactory.class);
        }
    }
}
